package com.mapbased.wfw.handler;

import java.io.*;
import java.nio.channels.*;
import javax.servlet.http.*;

import org.apache.commons.logging.*;
import org.dom4j.*;
import com.mapbased.wfw.*;

/**
 *
 * <p>Title: </p>
 *
 * <p>Description:
 * Request--->contains a If-modified-Since fileld ?
 * Yes:
 *     time in interval?
 *     Yes:Just respnose as no change,http code 304
 *     No: get last check time, past interval?
 *        Yes:recheck
 *        No: donothing
 *
 *           File newer than If-modified-Since time?
 *           yes:resend
 *           no: code 304
 * No:
 *      get last check time, past interval?
 *      Yes:recheck
 *      No:donothing
 *      send file
 *
 *
 *
 *
 *
 * </p>
 *
 * <p>Copyright: Copyright (c) 2004</p>
 *
 * <p>Company: mapbased.com</p>
 *
 * @author chy_hs(Baily)
 * @version 1.0
 */
public class ScgHandler
    extends PageHandler implements HttpConstants
{
    static protected Log log=LogFactory.getLog(ScgHandler.class);
    /* ------------------------------------------------------------ */
    /** General Fields.
     */


    /**
     * the interval to check if need to reexport,value in seconds
     */
    protected int defaultCheckInterval=5 * 60;
    //protected String pagePath;
    File basefile;

    protected void setPagePath(String s)
    {
        super.setPagePath(s);
        //  log.info(s);

        try
        {
            this.basefile=new File("mapbased-scg" + s);
            this.basefile.mkdirs();
            log.info(this.basefile.getAbsolutePath());
        }
        catch(Exception ex)
        {
            log.error(ex,ex);
        }

    }

    /**
     *
     * @param ele Element
     * @todo Implement this com.mapbased.wfw.PageHandler method
     */
    protected void fetchAttributs(Element ele)
    {
        Attribute a=ele.attribute("default-check-interval");
        if(a != null)
        {
            try
            {
                this.defaultCheckInterval=Integer.parseInt(a.getValue());
            }
            catch(NumberFormatException ex)
            {
                log.warn(ex.getMessage(),ex);

            }
            ele.remove(a);
        }
    }

    /**
     * should be abstract,code inside is juest to test
     * @param ctx RenderContext
     * @return String
     */
    protected String getKey(RenderContext ctx)
    {
        String id=ctx.getSureSingleParam("id").trim();
        //String.valueOf(id);
        if(id.length() == 0)
        {
            id="null";
        }

        return id;
    }

    /**
     *  check whether this generated static content is changed,
     * if changed,reexport to the file and return true
     * now;
     * when write to the scg file,remember to  lock it exclusive

     * @param rtx RenderContext
     * @param scgFile :the generated static content file
     * @return byte[]:if reexported,return the exported content bytes ,
     * else  return null;
     */
    protected byte[] checkToUpdate(RenderContext rtx,File scgFile)
        throws IOException
    {
        //  scgFile.writeUTF(rtx.getRequest().toString() + rtx.getLocale());
        return null;
    }

    protected int getCheckInterval(RenderContext ctx)
    {
        return this.defaultCheckInterval;
    }

    /**
     * filter the cached request
     * every identied request map to 2 files, a content file end with .scg
     * another is a flag file end with .update
     *
     * flag file tells the last check time
     * content file include the generated static content and tells the oldest generated time
     *
     * when check, the web thread lock exclusive to the flag file first,
     * verfy if the content is change,if is changed,lock the content file and update it
     *
     * @param ctx RenderContext
     * @return InputStream
     * @throws IOException
     */

    protected byte[] filterRequest(RenderContext ctx)
        throws IOException
    {
        String key=this.getKey(ctx);
        File f=new File(this.basefile,key.concat(".update"));
        File scgfile=new File(this.basefile,
                              key.concat(".scg"));
        long lastchecktime=f.lastModified();
        int s_interval=this.getCheckInterval(ctx);
        int i_interval=1000 * s_interval;
        HttpServletResponse response=ctx.getResponse();

        if(System.currentTimeMillis() - lastchecktime > i_interval ||
           !scgfile.exists())
        {
            java.io.RandomAccessFile raf=new java.io.RandomAccessFile(f,"rw");
            /// java.io.FileOutputStream fos=new java.io.FileOutputStream(f,true);
            byte[] content=null;

            FileLock lock=raf.getChannel().lock();
            try
            {
                long tmpl=f.lastModified();
                //    if(System.currentTimeMillis() - tmpl > i_interval) //tmpl == lastchecktime || lastchecktime == 0) //if not reenter while get lock

                if(tmpl == lastchecktime || lastchecktime == 0) //if not reenter while get lock

                {

                    content=this.checkToUpdate(ctx,scgfile); //if changed then content !=null

                    lastchecktime=System.currentTimeMillis();

                }
                else
                {
                    lastchecktime=tmpl;
                }
            }
            finally
            {

                lock.release();
                raf.close();
                f.setLastModified(lastchecktime);
            }
            if(content != null)
            {
                //quick return invoid to do more check and file read
                response.setHeader(this.__CacheControl,"max-age=" + s_interval);
                response.setDateHeader(this.__Expires,
                                       System.currentTimeMillis() + i_interval);
            //    response.setHeader(this.__CacheControl,"max-age");


                response.setDateHeader(this.__LastModified,lastchecktime);

                return content;
            }

        }
        /////////////
        response.setHeader(this.__CacheControl,"max-age=" + s_interval);
        response.setDateHeader(this.__Expires,
                               System.currentTimeMillis() + i_interval);

        long l=ctx.getRequest().getDateHeader(this.__IfModifiedSince);
        //   HttpServletResponse response=ctx.getResponse();
        if(l > 0)
        {

            if(l + 1000 >= lastchecktime)
            {
                this.returnUnmodifyed(response);

            }
            long lastupdate=scgfile.lastModified();
            if(l + 1000 >= lastupdate && lastupdate > 0)
            {
                this.returnUnmodifyed(response);
            }

        }
        //  response.setHeader(this.__CacheControl,"max-age=" + s_interval);
        response.setDateHeader(this.__LastModified,lastchecktime);

        //   response.setHeader(this.__Age,String.valueOf( this.defaultCheckInterval/2));



        FileInputStream fis=new FileInputStream(scgfile);

        FileLock lock=fis.getChannel().lock(0,Long.MAX_VALUE,true);
        //  long cl=f.length();
        // if(cl == 0)
        {
            //  log.warn("File length should not be zero!");
        }
        byte[] result=null;
        try
        {
            java.io.ByteArrayOutputStream bos=new java.io.ByteArrayOutputStream();
            int b;
            while((b=fis.read()) != -1)
            {
                bos.write(b);
            }
            result=bos.toByteArray();
            if(result.length == 0)
            {
                log.warn("File content should not be zero!");
            }

            // result=new byte[(int)cl];
            //fis.read(result);
        }
        finally
        {
            lock.release();
            fis.close();
        }

        return result;

    }

    /*
        protected java.io.InputStream filterRequest_old(RenderContext ctx)
            throws IOException
        {
            String key=this.getKey(ctx);
            File f=new File(this.basefile,key.concat(".scg"));
            File updateTimeFile=null;
            long lastchecktime=f.lastModified();
            if(System.currentTimeMillis() - lastchecktime >
               1000 * this.getCheckInterval(ctx))
            {
     java.io.RandomAccessFile raf=new java.io.RandomAccessFile(f,"rw");
                FileLock lock=raf.getChannel().lock();
                try
                {
                    long tmpl=f.lastModified();
     if(tmpl == lastchecktime || lastchecktime == 0) //if not reenter while get lock
                    {

                        boolean changed=this.checkToUpdate(ctx,raf);
                        lastchecktime=System.currentTimeMillis();
                        if(changed)
                        {
                            updateTimeFile=new File(this.basefile,
                                                    key.concat(".update"));
                            if(!updateTimeFile.setLastModified(lastchecktime))
                            {
                                updateTimeFile.createNewFile();
                            }
                        }
                        //set check time

                        f.setLastModified(lastchecktime);

                    }
                    else
                    {
                        lastchecktime=tmpl;
                    }
                }
                finally
                {

                    lock.release();
                    raf.close();
                }

            }

            long l=ctx.getRequest().getDateHeader(this.__IfModifiedSince);
            HttpServletResponse response=ctx.getResponse();
            if(l + 1000 >= lastchecktime)
            {
                this.returnUnmodifyed(response);

            }
            if(updateTimeFile == null)
            {
                updateTimeFile=new File(this.basefile,
                                        key.concat(".update"));

                long lastupdate=updateTimeFile.lastModified();
                if(l + 1000 >= lastupdate && lastupdate > 0)
                {
                    this.returnUnmodifyed(response);
                }

            }
            response.setDateHeader(this.__LastModified,lastchecktime);
            //   response.setHeader(this.__Age,String.valueOf( this.defaultCheckInterval/2));
            FileInputStream fis=new FileInputStream(f);
            fis.getChannel().lock(0,f.length(),true);
            return fis;

        }*/

    protected void returnUnmodifyed(HttpServletResponse response)
        throws IOException
    {
        // response.reset();
        response.setStatus(this.__304_Not_Modified);

        //  response.flushBuffer();
        throw ExitException.instance;

    }

}
